package server;

import java.io.File;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Hashtable;

import javax.print.attribute.HashAttributeSet;

import org.apache.commons.io.FileUtils;

import GUI.ResultDisplay;

import ocsf.server.ConnectionToClient;

import logic.Book;
import logic.Message;

public class BookController {

	private DB_Manager DB;
	private OBL_Server parent;
	
	public BookController(OBL_Server parent,DB_Manager DB) {
		this.parent = parent;
		this.DB = DB;
	}
	
	
	/**
	 * This function will search the receive a BookID and delete that book, and the copies
	 * The Result will be sent as a Message to the client
	 * @param received - the Message object containing the fields and their requested values
	 * @param client - the client which requested the search
	 */
	public void DeleteBook(Message received, ConnectionToClient client){
		
		ArrayList<Object> dataIn = received.GetMessageData();
		
		String BID = (String)dataIn.get(0);
		String deleteBook = "DELETE FROM BOOKS WHERE BID = " + BID;
		int result = DB.Update(deleteBook);
		
		ArrayList<String> index = new ArrayList<String>();
		ArrayList<Object> data = new ArrayList<Object>();
		
		if (result == 1){ //book deleted
			String deleteBookCopy = "DELETE FROM BOOKCOPIES WHERE BID = " + BID;
			int copiesResult = DB.Update(deleteBookCopy);
			
			//TODO delete book categories
			
			if (copiesResult == 1){
				index.add("RESULT");
				data.add("SUCCESS");
			}else{
				index.add("RESULT");
				data.add("ERROR - book copies were not delete");
				
				parent.Log("[DeleteBook] Unable to delete book copies, BID: " + BID + ". [" + client.getInetAddress().toString() + "]");
				return;
			}
		}else if (result == 0){ //error
			index.add("RESULT");
			data.add("ERROR - book was not deleted");

			parent.Log("[DeleteBook] Unable to delete book, BID: " + BID + ". [" + client.getInetAddress().toString() + "]");
			return;
		}
				

		int action = Message.DELETE_BOOK_RESPONSE;
		
		Message toSend = new Message(index, data, action);
		
		try{
			client.sendToClient(toSend);
		}catch(IOException e){
			parent.Log("[DeleteBook] IOException - sending result. [" + client.getInetAddress().toString() + "]");
			return;
		}
	}
	
	
	/**
	 * This function query the DB for all the bookcopies in the DB with the requested status
	 * @param status - the requested status to look for
	 * @return number of copies, -1 for failure
	 */
	public int GetNumberOfCopiesForStatus(int status){
		String query = "SELECT COUNT(BID) AS VAL FROM bookcopies WHERE STATUS = " + status + ";";
		ResultSet rs = DB.Query(query);
		try{
			rs.next();
			int val = rs.getInt("VAL");
			rs.close();
			return val;
		}catch(SQLException e){
			parent.Log("[GetNumberOfCopiesForStatus] SQLException, status: " + status);
			return -1;
		}catch(ClassCastException cl){
			parent.Log("[GetNumberOfCopiesForStatus] ClassCastException" + cl.getMessage());
			return 0;
		}catch(IndexOutOfBoundsException in){
			parent.Log("[GetNumberOfCopiesForStatus] IndexOutOfBoundsException" + in.getMessage());
			return -1;
		}
	}
	
	
	
	/**
	 * This function delete the BID,BCID from the bookcopies table
	 * and also updates books table for the new copies number
	 * @param bid - the BID to look for
	 * @param bcid - the BCID to look for
	 * @param wasAvailable - whether to reduce 1 from availablecopies or not
	 * @return 1 for success, 0 otherwise
	 */
	public int DeleteBookCopy(String bid,String bcid,int wasAvailable){
		String query = "DELETE FROM bookcopies WHERE BID = '" + bid + "' AND BCID = '" + bcid + "'";
		int res = DB.Update(query);
		if (res == 1){
			ResultSet rs = DB.Query("SELECT * FROM books WHERE BID = '" + bid + "'");
			try{
				while(rs.next()){
					int copies = rs.getInt("COPIES");
					int available = rs.getInt("AVAILABLECOPIES");
					copies--;
					available -= wasAvailable;
					if (DB.Update("UPDATE books SET COPIES = '" + copies + "' AND AVAILABLECOPIES = '" + available + "' WHERE BID = '" + bid + "'") == 0){

						parent.Log("[DeleteBookCopy] unable to set update query, bid: " + bid + ",bcid: " + bcid + ",wasAvailable: " + wasAvailable);
						rs.close();
						return 0;
						
					}else return 1;
				}
				rs.close();
			}catch(SQLException e){
				parent.Log("[DeleteBookCopy] SQLException, bid: " + bid + ",bcid: " + bcid + ",wasAvailable: " + wasAvailable + ". " + e.getMessage());
				return 0;
			}catch(ClassCastException cl){
				parent.Log("[DeleteBookCopy] ClassCastException" + cl.getMessage());
				return 0;
			}
		}else{
			parent.Log("[DeleteBookCopy] Unable to delete from bookcopies table, bid: " + bid + ",bcid: " + bcid + ",wasAvailable: " + wasAvailable);
			return 0;
		}
		return 0;
	}
	
	/**
	 * This function will search the DB for Book by the requested fields
	 * The Result will be sent as a Message to the client
	 * @param received - the Message object containing the fields and their requested values
	 * @param client - the client which requested the search
	 */
	public void SearchBook(Message received, ConnectionToClient client){
				
		ResultSet rs = QueryBook(received.GetMessageIndexes(), received.GetMessageData());
		ArrayList<Book> bookList = SeralizeResultSet(rs);
		
		try{
			ArrayList<String> index = new ArrayList<String>();
			index.add("INDICATOR");
			index.add("RESULTSET");
			index.add("DIRECTOR");
			ArrayList<Object> data = new ArrayList<Object>();
			data.add(ResultDisplay.BOOK);
			data.add(bookList);
			data.add(received.GetMessageData().get(0));
			
			int action = Message.SEARCH_BOOK_RESPONSE;
			
			Message toSend = new Message(index, data, action);
			
			try{
				client.sendToClient(toSend);
			}catch(IOException e){
				parent.Log("[SearchBook] IOException, send messsage. [" + client.getInetAddress() + "]." + e.getMessage());
				return;
			}
		}catch(IndexOutOfBoundsException in){
			parent.Log("[SearchBook] IndexOutOfBoundsException" + in.getMessage());
			return;
		}

		
	}
	
	
	/**
	 * This function verifies if the entered book is in the 
	 * entered catefory
	 * @param BID - the BID to look for
	 * @param Category - the Catergory to look for
	 * @return true or false
	 */
	public boolean isBookInCategory(int BID,String Category){
		
		ArrayList<String> catList = GetBookCategories(Integer.valueOf(BID));
		for(String cat : catList){
			if (cat.equals(Category))return true;
		}
		return false;
		
	}
	
	
	/**
	 * This function retrieve all the categories a book has
	 * @param BID - the book to query for
	 * @return ArrayList<String> containing all the categories for the entered book
	 */
	private ArrayList<String> GetBookCategories(int BID){
		ArrayList<String> categories = new ArrayList<String>();
		
		String query = "SELECT * FROM bookcategories WHERE BID = " + BID;
		ResultSet rs = DB.Query(query);
		
		try{
			while(rs.next()){
				categories.add(rs.getString("CATEGORY"));
			}
			rs.close();
		}catch(SQLException e){
			parent.Log("[GetBookCategories] SQLException, BID: " + BID + ". " + e.getMessage());
			return null;
		}catch(ClassCastException cl){
			parent.Log("[GetBookCategories] ClassCastException" + cl.getMessage());
			return null;
		}
		
		
		return categories;	
	}
	
	
	
	/**
	 * This function receives a book id, and returns an ArrayList<String> with it's authors
	 * @param BID - the BID to look for
	 * @return ArrayList<String> containing all the book's authors
	 */
	public ArrayList<String> GetAuthorsForBook(int BID){
		String query = "SELECT AUTHOR FROM books WHERE BID = " + BID;
		ResultSet rs = DB.Query(query);
		ArrayList<String> authList = new ArrayList<String>();
		
		try{
			while(rs.next()){
				String author = "";
				author = rs.getString("AUTHOR");
				while((author != null) && (author.indexOf(",") > 0)){
					String temp = author.substring(0, author.indexOf(",")-1);
					authList.add(temp);
					author = author.substring(author.indexOf(",")+2,author.length());
				}
				if(author != null) authList.add(author);

				return authList;
			}
		}catch(SQLException e){
			parent.Log("[GetAuthorsForBook] SQLException, BID: " + BID + ". " + e.getMessage());
			return null;
		}catch(IndexOutOfBoundsException in){
			parent.Log("[GetAuthorsForBook] IndexOutOfBoundsException, BID: " + BID + ". " + in.getMessage());
			return null;
		}
		
		return null;
	}
	
	/**
	 * This Function convert a ResultSet into ArrayList<Book> for sending as a Message
	 * (ResultSet is not seralizeable)
	 * @param rs - the ResultSet to conver
	 * @return - ArrayList<Book> which conatins all the books in the ResultSet
	 */
	public ArrayList<Book> SeralizeResultSet(ResultSet rs){
		
		LoanController loanController = new LoanController(parent, DB);
		ArrayList<Book> bookList = new ArrayList<Book>();
		
		try{
			while(rs.next()){
				ArrayList<String> categories = GetBookCategories(rs.getInt("BID"));
				ArrayList<String> author = GetAuthorsForBook(rs.getInt("BID"));
				//Book(int BID,String name,int edition,String printDate,String purchaseDate,int hotBook,int copies, 
					//int availableCopies, String description,String location,String bookIndex,ArrayList<String> categories,String nextBook,ArrayList<String> authors)
				String nextDate = null;
				if(rs.getInt("AVAILABLECOPIES") == 0) nextDate = loanController.GetNextDateForBook(rs.getInt("BID"));
				
				Book book = new Book(rs.getInt("BID"),rs.getString("NAME"),rs.getInt("EDITION"),rs.getString("PRINT_DATE"),rs.getString("PURCHASE_DATE"),
						rs.getInt("HOT_BOOK"),rs.getInt("COPIES"),rs.getInt("AVAILABLECOPIES"),rs.getString("DESCRIPTION"),rs.getString("LOCATION"),
						rs.getString("BOOK_INDEX"),categories,nextDate,author);
				bookList.add(book);
			}
			rs.close();
			return bookList;
		}catch(SQLException e){
			parent.Log("[SeralizeResultSet] SQLException" + e.getMessage());
			return null;
		}catch(ClassCastException cl){
			parent.Log("[SeralizeResultSet] ClassCastException" + cl.getMessage());
			return null;
		}
	}
	
	/**
	 * This function sets the availablecopies number for the selected BID
	 * @param bid - the BID to update
	 * @param newValue - the new value of availablecopies
	 * @return 1 for success, 0 otherwise
	 */
	public int setAvailableCopies(String bid,int newValue){
		return DB.Update("UPDATE books SET AVAILABLECOPIES = '" + newValue + "' WHERE BID = '" + bid + "'");
	}
	
	
	
	
	/**
	 * This function query the DB for the bid, and returns the number of available copies
	 * @param bid - the BID to look for
	 * @return number of available copies
	 */
	public int getAvailableCopies(String bid){
		String query = "SELECT * FROM books WHERE BID = " + bid;
		
		ResultSet rs = DB.Query(query);
		try {
			while(rs.next()){
				int retValue = rs.getInt("AVAILABLECOPIES");
				rs.close();				
				return retValue;
			}
			
			rs.close();
		} catch (SQLException e) {
			parent.Log("[getAvailableCopies] SQLException" + e.getMessage());
			return -1;
		}catch(ClassCastException cl){
			parent.Log("[getAvailableCopies] ClassCastException" + cl.getMessage());
			return -1;
		}
		
		return -1;
	}
	
	
	
	/**
	 * This function query the DB for the bid, and returns the name of the book
	 * @param bid - the BID to look for
	 * @return The book's name
	 */
	public String getNameForBook(String bid){
		String query = "SELECT * FROM books WHERE BID = " + bid;
		
		ResultSet rs = DB.Query(query);
		try {
			while(rs.next()){
				String name = rs.getString("NAME");
				rs.close();				
				return name;
			}
			
			rs.close();
		} catch (SQLException e) {
			parent.Log("[getNameForBook] SQLException" + e.getMessage());
			return null;
		}catch(ClassCastException cl){
			parent.Log("[getNameForBook] ClassCastException" + cl.getMessage());
			return null;
		}
		
		return null;
	}
	
	
	
	
	
	/**
	 * This function finds the first available BCID and returns it
	 * (It DOES NOT set it to be loaned, just returns it - 
	 * please call SetBCIDStatus(String bid, int bcid, int value) at end of loan)
	 * @param bid - the BID to look for
	 * @return the BCID, or -1 if such was not found
	 */
	public int AllocateBCID(String bid){
		String query = "SELECT * FROM bookcopies WHERE BID = " + bid + 
				" AND STATUS = " + Book.BOOK_COPY_AVAILABLE;
		
		ResultSet rs = DB.Query(query);
		try{
			while(rs.next()){
				int retValue =  rs.getInt("BCID");
				rs.close();
				return retValue;
			}
			rs.close();
		}catch(SQLException e){
			parent.Log("[AllocateBCID] SQLException" + e.getMessage());
			return -1;
		}catch(ClassCastException cl){
			parent.Log("[AllocateBCID] ClassCastException" + cl.getMessage());
			return -1;
		}
		return -1;
	}
	
	
	
	/**
	 * This function sets the desired value as the status in BOOKCOPIES table
	 * @param bid - the BID value
	 * @param bcid - the BCID value
	 * @param value - the value to set
	 * @return 1 for success, 0 for failure
	 */
	public int SetBCIDStatus(String bid,int bcid,int value){
		String query = "UPDATE bookcopies SET STATUS = '" + value + "' WHERE BID = '"
				+ bid + "' AND BCID = '" + bcid + "'";
		
		return DB.Update(query);
	}
	
	
	
	/**
	 * This function verifies whether a specified book copy is loaned
	 * @param bid - the book id
	 * @param bcid - the book copy id
	 * @return true if its loaned, false otherwise
	 */
	public boolean isBCIDLoaned(String bid,String bcid){
		String query = "SELECT * FROM bookcopies WHERE BID = " + bid + " AND BCID = " + bcid +
				" AND STATUS = " + Book.BOOK_COPY_NOT_AVAILABLE;
		ResultSet rs = DB.Query(query);
		
		try{
			while(rs.next()){
				rs.close();
				return true;
			}
			rs.close();
		}catch(SQLException e){
			parent.Log("[isBCIDLoaned] SQLException" + e.getMessage());
			return false;
		}
		return false;
	}
	
	
	
	/**
	 * This function query the DB for the entered BID and verifies if its a HOT_BOOK or not
	 * @param bid - the BID to look for
	 * @return true for HotBook, false otherwise
	 */
	public boolean isHotBook(String bid){
		String query = "SELECT * FROM books WHERE BID = " + bid;
		
		ResultSet rs = DB.Query(query);
		try{
			while(rs.next()){
				if (rs.getInt("HOT_BOOK") == 1) {
					rs.close();
					return true;
				}
			}
			rs.close();
		}catch(SQLException e){
			parent.Log("[isHotBook] SQLException" + e.getMessage());
			return false;
		}catch(ClassCastException cl){
			parent.Log("[isHotBook] ClassCastException" + cl.getMessage());
			return false;
		}
		return false;
	}
	
	
	
	/**
	 * This Function query the DB for the entered BID and validate that such book exists
	 * @param bid - the BID to look for
	 * @return - true if exists, false otherwise
	 */
	public boolean ValidateBook(String bid){
		String query = "SELECT * FROM books WHERE BID = " + bid;
		
		ResultSet rs = DB.Query(query);
		try {
			while(rs.next()){
				rs.close();
				return true;
			}
			rs.close();
		}catch (SQLException e) {
			parent.Log("[ValidateBook] SQLException" + e.getMessage());
			return false;
		}
		
		return false;
	}
	
	
	public void SendPDF(Message msg,ConnectionToClient client){
		ArrayList<Object> data = msg.GetMessageData();
		
		int BID = (Integer)data.get(0);
		
		byte[] fileByte = null;
	    try {
	    	File f = new File("INDEXES/" + BID + ".pdf");
			fileByte = FileUtils.readFileToByteArray(f);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	    
	    ArrayList<String> indOut = new ArrayList<String>();
	    ArrayList<Object> dataOut = new ArrayList<Object>();
	    
	    int action = Message.PDF_RESPONSE;
	    
	    dataOut.add(BID);
	    dataOut.add(fileByte);
	    
	    Message toSend = new Message(indOut,dataOut,action);
	    
	    try{
	    	client.sendToClient(toSend);
	    }catch(IOException e){
	    	parent.Log("PDF OUT ERR");
	    }
	}
	
	
	/**
	 * This Function query the DB for the entered BID,BCID and validate that such book copy exists
	 * @param bid - the BID to look for
	 * @param bcid - the BCID to look for
	 * @return - true if exists, false otherwise
	 */
	public boolean ValidateBookCopy(String bid,String bcid){
		String query = "SELECT * FROM bookcopies WHERE BID = " + bid + " AND BCID = " + bcid;
		
		ResultSet rs = DB.Query(query);

		try {
			while(rs.next()){
				rs.close();
				return true;
			}
			rs.close();
		} catch (SQLException e) {
			parent.Log("[ValidateBookCopy] SQLException" + e.getMessage());
			return false;			
		}
		
		return false;
	}
	
	
	/**
	 * This functino querys the DB for UID and return it's EMail address
	* @param UID - the UID to look for
	* @return - String with the users EMail address
	*/
	public String GetMailForMember(int UID){
		String query = "SELECT * FROM users where UID = " + UID;
		ResultSet rs = DB.Query(query);	
		String retVal = null;

		try{
			while(rs.next()){
			retVal = rs.getString("MAIL");
			rs.close();
		}
		}catch(SQLException e){
			parent.Log("[GetMailForMember] SQLException" + e.getMessage());
			return null;
		}catch(ClassCastException cl){
			parent.Log("[isHotBook] ClassCastException" + cl.getMessage());
			return null;
		}
		return retVal;
	}

	
	/**
	 * This function querys the DB for all selected categories
	 * @param categories - the categories to look for
	 * @return ArrayList<Book> with all qualifying books
	 
	private ArrayList<Book> GetBooksForCategories(ArrayList<String> categories){
		try{
			String query = "SELECT * from bookcategories WHERE BID = BID";
			if (categories.size() > 0){
				query += " AND (CATEGORY = '" + categories.get(0) + "'";
				for(int i=1;i<categories.size(); i++){
					query += " OR CATEGORY = '" + categories.get(i) + "'";
				}
				query += ");";
			}
			ResultSet rs = DB.Query(query);
			return SeralizeResultSet(rs);	
		}catch(IndexOutOfBoundsException in){
			parent.Log("[GetBooksForCategories] IndexOutOfBoundsException" + in.getMessage());
			return null;
		}
		
	}
	
	
	
/*	*//**
	 * This function querys the DB for all selected authors
	 * @param aurhoes - the authors to look for
	 * @return ArrayList<Book> with all qualifying books
	 *//*
	private ArrayList<Book> GetBooksForAuthors(ArrayList<String> authors){
		String query = "SELECT * from bookauthors WHERE BID = BID";
		if (authors.size() > 0){
			query += " AND (AUTHOR_NAME = '" + authors.get(0) + "'";
			for(int i=1;i<authors.size(); i++){
				query += " OR AUTHOR_NAME = '" + authors.get(i) + "'";
			}
			query += ");";
		}
		ResultSet rs = DB.Query(query);
		return SeralizeResultSet(rs);
	}*/
	
	
	/**
	 * This function is private, meant to query the actual DB for the fields and return the ResultSet
	 * @param fields - the fields to query by
	 * @param data - the data to query by
	 * @return ResultSet which contains all qualified books
	 */
	private ResultSet QueryBook(ArrayList<String> fields,ArrayList<Object> data){
		
		String query = "SELECT * FROM books";
		try{
			if (fields.size() > 1) {
				String field = (String)fields.get(1);
				if (!field.equals("AUTHORS")){
					
					if ((field.equals("PRINT_DATE")) || (field.equals("PURCHASE_DATE")) || (field.equals("LOCATION")))
							 query += " WHERE " + field + " = '" + (String)data.get(1) + "'";
					else if (field.equals("EDITION")) query += " WHERE " + field + " = '" + (Integer)data.get(1) + "'";
					else query += " WHERE " + field + " = '" + (String)data.get(1) + "'";
					
					for(int i=2; i < fields.size() - 2; i++){ //-2 is for Authors, -1 for category
						field = fields.get(i);
						
						if ((field.equals("PRINT_DATE")) || (field.equals("PURCHASE_DATE")) || (field.equals("LOCATION")))
							 query += " AND " + field + " = '" + (String)data.get(i) + "'";
					else query += " AND " + field + " = '" + (String)data.get(i) + "'";
					}
					String auth = (String)data.get(data.size() - 2);
					if (!auth.equals("")) query += " AND " + (String)data.get(data.size() - 2) + ";";
				}else{
					String auth = (String)data.get(data.size() - 2);
					if (!auth.equals("")) query += " WHERE " + (String)data.get(data.size() - 2) + ";";
				}
				
			}
			
			return DB.Query(query);
			
		}catch(ClassCastException cl){
			parent.Log("[QueryBook] ClassCastException" + cl.getMessage());
			return null;
		}catch(IndexOutOfBoundsException in){
			parent.Log("[QueryBook] IndexOutOfBoundsException" + in.getMessage());
			return null;
		}

	}
	
	
	/**
	 * This Function updates the Book object in the DB
	 * @param book - the Message object containing the book's information
	 * @param client - Client which created the book
	 * @return 1 for valid, 0 for error
	 */
	public int UpdateBook(Message book,ConnectionToClient client){
		
		try{
			ArrayList<Object> data = book.GetMessageData();
			ArrayList<String> index = book.GetMessageIndexes();
					
			int BID = (Integer)data.get(11);
			int bkIndx = -1;
			//Book does not exist, ok to add!
			String updateQuery = "UPDATE BOOKS SET " + (String)index.get(0) + " = '" + (String)data.get(0) + "'";
			for (int i=1; i< data.size() - 1; i++){
				if(!index.get(i).equals("BOOK_INDEX")) updateQuery += ", " + (String)index.get(i) + " = '" + (String)data.get(i) + "'";
				else bkIndx = i;
			}
			
			updateQuery += " WHERE BID = '" + BID + "';";
			if (DB.Update(updateQuery) == 0){
				parent.Log("[UpdateBook] Update book query failed: " + updateQuery + ". [" + client.getInetAddress() + "].");
				return 0;
			}
			
			String removeCategories = "DELETE FROM bookcategories WHERE BID = " + BID;
			if (DB.Update(removeCategories) == 0){
				parent.Log("[UpdateBook] delete from bookcategories failed: " + removeCategories + ". [" + client.getInetAddress() + "].");
				return 0;
			}
			
			ArrayList<String> catList = (ArrayList<String>)data.get(10);
			for(String cat : catList){
				String catInsert = "INSERT INTO BOOKCATEGORIES (id,BID,CATEGORY) VALUES)" + "(null,'" + BID + "','" + cat + "')";
				if (DB.Update(catInsert) == 0){
					parent.Log("[UpdateBook] insert into bookcategories failed: " + catInsert + ". [" + client.getInetAddress() + "].");
					return 0;
				}
			}
			
			if (bkIndx != -1){
				byte[] byteArray = (byte[])data.get(bkIndx);
				String indexPath = "INDEXES/" + BID + ".pdf";
				File f = new File(indexPath);
				try {
					FileUtils.writeByteArrayToFile(f, byteArray);
				} catch (IOException e) {
					parent.Log("[UpdateBook] FileWrite IOException" + e.getMessage());
					return 0;
				}
			}
			
			//TODO update bookcopies table
			
			SendOutputToUser("OK","Book Updated",Message.UPDATE_BOOK_RESPONSE,client);

			return 1;
		}catch(IndexOutOfBoundsException in){
			parent.Log("[UpdateBook] IndexOutOfBoundsException" + in.getMessage());
			return 0;
		}
		
			
	}
	
	/**
	 * This Function creates the Book object in the DB
	 * @param book - the Message object containing the book's information
	 * @param client - Client which created the book
	 */
	public void CreateBook(Message book,ConnectionToClient client){
		
		ArrayList<Object> data = book.GetMessageData();
		
		ResultSet rs = DB.Query("SELECT * FROM books WHERE NAME = '" + (String)data.get(0) + 
				"' AND EDITION = '"+ (String)data.get(2) + "' AND PRINT_DATE = '" + (String)data.get(3) + "';");
				
		try{
			while(rs.next()){
				SendOutputToUser("ERROR","Book already exists!",Message.ADD_BOOK_RESPONSE,client);
				rs.close();
				return;
			}
			rs.close();
			
			int indexVal;
			if ((byte[])data.get(9) != null) indexVal = 1;
			else indexVal = 0;
			

			String insertQuery = "INSERT INTO BOOKS " +
					"(BID, NAME, EDITION, PRINT_DATE, PURCHASE_DATE, HOT_BOOK, COPIES, AVAILABLECOPIES, DESCRIPTION, LOCATION, BOOK_INDEX,AUTHOR) VALUES" +
					"(null,'" + (String)data.get(0) + "','" + (String)data.get(2) + "','" + (String)data.get(3) + 
					"','" + (String)data.get(4) + "','" + (String)data.get(5) + "','" + (String)data.get(6) + "','" + (String)data.get(6) + "','" + (String)data.get(7) + "','"
					+ (String)data.get(8) + "','" + indexVal + "','" + (String)data.get(1) + "');";
			
			int newBid = DB.QueryAutoIncrement(insertQuery);
			
			if(newBid == -1){
				parent.Log("Create book failed. [" + client.getInetAddress().toString() + "] - " + (String)data.get(0));
				return;
			}
			
			if (indexVal != 0){
				byte[] byteArray = (byte[])data.get(9);
				String indexPath = "INDEXES/" + newBid + ".pdf";
				File f = new File(indexPath);
				try {
					FileUtils.writeByteArrayToFile(f, byteArray);
				} catch (IOException e) {
					parent.Log("PDF File createion(Server Side) error. [" + client.getInetAddress().toString() + "] " + e.getMessage());
					return;
				}
			}

			int copies = Integer.parseInt((String)data.get(6));
			String purchase = (String)data.get(4);
			for(int i=0; i<copies; i++){
				if (DB.Update("INSERT INTO BOOKCOPIES (BCID,BID,PURCHASE_DATE,STATUS) VALUES" +
						"(null,'" + newBid + "','" + purchase + "','" + Book.BOOK_COPY_AVAILABLE +"')") == 0){
					parent.Log("[CreateBook] Insert into bookcopies failed.BID = " + newBid + ", " + copies + " copies. [" + client.getInetAddress().toString() + "]");
					return;
				}
			}
			
			ArrayList<String> catList = (ArrayList<String>)data.get(10);
			for(String cat : catList){
				if (DB.Update("INSERT INTO BOOKCATEGORIES (id,BID,CATEGORY) VALUES" +
						"(null,'" + newBid + "','" + cat + "')") == 0){
					parent.Log("[CreateBook] Insert into bookcopies failed.BID = " + newBid + ", " + copies + " copies. [" + client.getInetAddress().toString() + "]");
					return;
				}
			}
			
			SendOutputToUser("OK","Book Created",Message.ADD_BOOK_RESPONSE,client);
			
			return;
			
		}catch(SQLException e){
			parent.Log("[CreateBook] SQLException. [" + client.getInetAddress().toString() + "] " + e.getMessage());
			return;
		}catch(IndexOutOfBoundsException in){
			parent.Log("[CreateBook] Index out of bounds. [" + client.getInetAddress().toString() + "] " + in.getMessage());
			return;
		}catch(ClassCastException cl){
			parent.Log("[CreateBook] Class cast exception. [" + client.getInetAddress().toString() + "] " + cl.getMessage());
			return;
		}
	}
	
	
	
	/**
	 * This message will handle message output to the user
	 * (When only needs to send a text message without extra data)
	 * @param status - String - "OK" or "ERROR"
	 * @param message - String - containing the message
	 * @param action - int - the message type
	 * @param client - the client to send to
	 */
	private void SendOutputToUser(String status,String message,int action,ConnectionToClient client){
		
		ArrayList<String> indexOUT = new ArrayList<String>();
		ArrayList<Object> dataOUT = new ArrayList<Object>();
		
		indexOUT.add(status);
		dataOUT.add(message);
		Message toSend = new Message(indexOUT,dataOUT,action);
		try{
			client.sendToClient(toSend);	
		}catch(IOException e){
			parent.Log("[SendOutputToUser] IOException. [" + client.getInetAddress().toString() + "]");
		}

	}

}
